#include "FadeShader.h"

using namespace Graphics;

RTTI_DEFINITIONS(FadeShader);

FadeShader::FadeShader(void)
	:Shader("FadeShader",""),
	 mFadeBuffer(NULL)
{
}

FadeShader::~FadeShader(void)
{
}

bool FadeShader::Initialize(HWND hwnd)
{
	InitializeShader(hwnd, "..\\..\\assets\\shaders\\fade.vs","..\\..\\assets\\shaders\\fade.ps");

	return true;
}

void FadeShader::Deinitialize()
{
	DeinitializeShader();
}

bool FadeShader::Render(int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix,  
				ID3D11ShaderResourceView* texture, float fadeAmount)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();

	SetShaderParameters(indexCount, worldMatrix, viewMatrix, projectionMatrix, texture, fadeAmount);

	RenderShader(device, indexCount);

	return true;
}

bool FadeShader::InitializeShader(HWND hwnd, const char* vsFilename, const char*  psFilename)
{

	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	if(Shader::InitializeShader(device, hwnd, vsFilename, psFilename))
	{
		// Now setup the layout of the data that goes into the shader.
		// This setup needs to match the VertexType stucture in the ModelClass and in the shader.
		D3D11_INPUT_ELEMENT_DESC polygonLayout[3];
		polygonLayout[0].SemanticName = "POSITION";
		polygonLayout[0].SemanticIndex = 0;
		polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
		polygonLayout[0].InputSlot = 0;
		polygonLayout[0].AlignedByteOffset = 0;
		polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[0].InstanceDataStepRate = 0;

		polygonLayout[1].SemanticName = "COLOR";
		polygonLayout[1].SemanticIndex = 0;
		polygonLayout[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
		polygonLayout[1].InputSlot = 0;
		polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
		polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[1].InstanceDataStepRate = 0;

		polygonLayout[2].SemanticName = "TEXCOORD";
		polygonLayout[2].SemanticIndex = 0;
		polygonLayout[2].Format = DXGI_FORMAT_R32G32_FLOAT;
		polygonLayout[2].InputSlot = 0;
		polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
		polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[2].InstanceDataStepRate = 0;

		// Get a count of the elements in the layout.
		unsigned int  numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

		// Create the vertex input layout.
		HRESULT result = device->GetDevice()->CreateInputLayout(polygonLayout, numElements, mVertexShaderBuffer->GetBufferPointer(), mVertexShaderBuffer->GetBufferSize(), &mLayout);
		if(FAILED(result))
		{
			return false;
		}

		// Release the vertex shader buffer and pixel shader buffer since they are no longer needed.
		mVertexShaderBuffer->Release();
		mVertexShaderBuffer = NULL;

		mPixelShaderBuffer->Release();
		mPixelShaderBuffer = NULL;

			// Create a texture sampler state description.
		D3D11_SAMPLER_DESC samplerDesc;
		samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
		samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
		samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
		samplerDesc.MipLODBias = 0.0f;
		samplerDesc.MaxAnisotropy = 1;
		samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
		samplerDesc.BorderColor[0] = 0;
		samplerDesc.BorderColor[1] = 0;
		samplerDesc.BorderColor[2] = 0;
		samplerDesc.BorderColor[3] = 0;
		samplerDesc.MinLOD = 0;
		samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

		// Create the texture sampler state.
		result = device->GetDevice()->CreateSamplerState(&samplerDesc, &mSampleState);
		if(FAILED(result))
		{
			return false;
		}

		// Setup the description of the fade dynamic constant buffer that is in the vertex shader.
		D3D11_BUFFER_DESC fadeBufferDesc;
		fadeBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
		fadeBufferDesc.ByteWidth = sizeof(FadeBufferType);
		fadeBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		fadeBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		fadeBufferDesc.MiscFlags = 0;
		fadeBufferDesc.StructureByteStride = 0;

		// Create the constant buffer pointer so we can access the pixel shader constant buffer from within this class.
		result = device->GetDevice()->CreateBuffer(&fadeBufferDesc, NULL, &mFadeBuffer);
		if(FAILED(result))
		{
			return false;
		}

		return true;
	}
	return false;
}

void FadeShader::DeinitializeShader()
{
	Shader::DeinitializeShader();
	if(mFadeBuffer)
	{
		mFadeBuffer->Release();
		mFadeBuffer = NULL;
	}
}

bool FadeShader::SetShaderParameters(int indexCount, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix,  
				ID3D11ShaderResourceView* texture, float fadeAmount)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();

	Shader::SetShaderParameters(device, worldMatrix, viewMatrix, projectionMatrix);

	// Lock the fade constant buffer so it can be written to.
	D3D11_MAPPED_SUBRESOURCE  mappedResource;
	HRESULT result = device->GetDeviceContext()->Map(mFadeBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if(FAILED(result))
	{
		return false;
	}

	// Set shader texture resource in the pixel shader.
	device->GetDeviceContext()->PSSetShaderResources(0, 1, &texture);

	// Get a pointer to the data in the fade constant buffer.
	FadeBufferType* shaderData = (FadeBufferType*)mappedResource.pData;

	// Copy the fade amount into the fade constant buffer.
	shaderData->FadeAmount = fadeAmount;
	shaderData->Padding = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

	// Unlock the fade constant buffer.
	device->GetDeviceContext()->Unmap(mFadeBuffer, 0);

	// Set the position of the fade constant buffer in the pixel shader.
	unsigned int bufferNumber = 0;

	// Now set the fade constant buffer in the pixel shader with the updated values.
	device->GetDeviceContext()->PSSetConstantBuffers(bufferNumber, 1, &mFadeBuffer);

	return true;
}